package cn.qicaiwang.open.framework.configuration;

import cn.qicaiwang.open.common.constant.Constants;
import cn.qicaiwang.open.common.enums.ResultStatusCodeEnum;
import cn.qicaiwang.open.common.response.ResponseResult;
import cn.qicaiwang.open.framework.security.configuration.MobileAuthenticationConfig;
import cn.qicaiwang.open.framework.security.filter.ImageCaptchaCodeValidateFilter;
import cn.qicaiwang.open.framework.security.filter.JsonUsernamePasswordAuthenticationFilter;
import cn.qicaiwang.open.framework.security.filter.JwtAuthenticationFilter;
import cn.qicaiwang.open.framework.security.handler.CustomLogoutHandler;
import cn.qicaiwang.open.framework.security.handler.CustomLogoutSuccessHandler;
import cn.qicaiwang.open.framework.security.service.WebUserDetailService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 为了解决退出重新登录问题
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }
    /**
     * 表单登录查询用户逻辑
     */
    @Autowired
    private WebUserDetailService webUserDetailService;

    /**
     * 登录成功处理器
     */
    @Autowired
    private AuthenticationSuccessHandler customAuthenticationSuccessHandler;

    /**
     * 登录失败处理器
     */
    @Autowired
    private AuthenticationFailureHandler customAuthenticationFailureHandler;

    /**
     * 退出处理器
     */
    @Autowired
    private CustomLogoutHandler customLogoutHandler;

    /**
     * 退出成功处理器
     */
    @Autowired
    private CustomLogoutSuccessHandler customLogoutSuccessHandler;

    /**
     * 手机验证码余姚配置器
     */
    @Autowired
    private MobileAuthenticationConfig mobileAuthenticationConfig;

    @Autowired
    private ImageCaptchaCodeValidateFilter imageCaptchaCodeValidateFilter;

    /**
     * 密码加密器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * json用户名密码登录
     * @return
     * @throws Exception
     */
    @Bean
    public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter() throws Exception {
        JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter();
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(super.authenticationManagerBean());
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl(Constants.USERNAME_PASSWORD_LOGIN_URL);
        return jsonUsernamePasswordAuthenticationFilter;
    }

    @Bean
    public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationFilter();
    }

    //定义认证规则
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(webUserDetailService);
    }

    //安全拦截机制（最重要）
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.apply(mobileAuthenticationConfig)
            .and()
                .addFilterBefore(imageCaptchaCodeValidateFilter, UsernamePasswordAuthenticationFilter.class) //在用户名密码验证前加入验证码的过滤器
                .addFilterBefore(jsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                .successHandler(customAuthenticationSuccessHandler)
                .failureHandler(customAuthenticationFailureHandler)
                .loginProcessingUrl(Constants.USERNAME_PASSWORD_LOGIN_URL)
            .and()
                .logout()
                .logoutUrl("/u/logout")
                .addLogoutHandler(customLogoutHandler)
                .logoutSuccessHandler(customLogoutSuccessHandler)
                .deleteCookies("JSESSIONID")
            .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                .antMatchers("/u/**", "/oauth/**").permitAll()
                .antMatchers("/captcha/**").permitAll()
                .antMatchers("/crm/websocket").permitAll()
                .antMatchers("/crm/chat/**").permitAll()
                .antMatchers("/qcwapp/**").permitAll()
                .antMatchers("/wechat/portal/**").permitAll()
                .antMatchers("/sms/sendMessage").permitAll()
                .antMatchers("/crm/recruit/signature/**").permitAll()
                .antMatchers(Constants.RESOURCE_PREFIX + "/**").permitAll()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()//除了/r/**，其它的请求可以访问
            .and()
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint((request,response,authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    out.write(JSON.toJSONString(ResponseResult.error(ResultStatusCodeEnum.USER_NOLOGIN)));
                    out.flush();
                    out.close();
                })
            .and()
                .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)
        ;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(Constants.RESOURCE_PREFIX + "/**");
    }
}
